import traci
import rsi

from Street import *

class TLSController:
    
    def __init__(self, tlsId) :
        #Store TLS id to further reference
        self.tlsId = tlsId
        
        position = traci.junction.getPosition(tlsId);
        
        """TODO"""
        """x = position[0];"""
        """y = position[1];"""
        x = position[0];
        y = position[1];
        
        #Store the first phase duration
        self._cppControler = rsi.TLSController(tlsId, int(x), int(y))
       
        #Next step that a phase switch will occur on this tls
        self.nextSwitch = traci.trafficlights.getNextSwitch(tlsId)/1000
        
        #Index of current phase
        self.currentPhase = traci.trafficlights.getPhase(tlsId);
        
        #Time when the last switch happened
        self.lastPhaseSwitchStep = 0
        
        #Load a list o controlled lanes
        lanes = traci.trafficlights.getControlledLanes(self.tlsId)
        
        self._streets = {}
        self._buildStreets(lanes)
        
        # A flowAccumulator are used to aggregate values of induction loop.
        # Each aggregator has the following structure:
        #     { accumulatedValue : <val>, street : <street_object> }
        self._flowAccumulators = []
        self._lane2FlowAccumulators = {}
        self._buildAccumulators()

        self._phases = traci.trafficlights.getCompleteRedYellowGreenDefinition(tlsId)
     
        self._links = traci.trafficlights.getControlledLinks(tlsId)
        
        print self._links
        
    def onStep(self, step) :
        #Check if step was changed
        if step - self.lastPhaseSwitchStep > self.nextSwitch :
            self._trySwitchPhase(step)
            
        #Accumulate induction loop values
        self._accumulateFlowValue()
            
    def _trySwitchPhase(self, step) : 
        phaseIndex = traci.trafficlights.getPhase(self.tlsId)
        if self.currentPhase != phaseIndex :
            self._onPhaseChange(step, phaseIndex)
            
    """
      This method is executed on a phase start. Phase are identified by
      phaseIndex and the timestep when this event happend is stored in
      step variable.
    """
    def _onPhaseStart(self, phaseIndex, step) :
        print "phase {0} just started".format(phaseIndex)
        
        #Here I get phase increment that was calculated on last time
        #this phase was executed
        increment = self._cppControler.getPhaseIncrement(phaseIndex);
        print "#########Increment phase {0}: {1}############".format(phaseIndex, increment);
        
    def _onPhaseEnd(self, phaseIndex, step) :
        print "phase {0} just ended".format(phaseIndex)
        
        #When a phase ends, We collect data for the last phase.
        self._collectData()
        self._dumpStreets()
        
        #On and start calculation increment to next time this phase is 
        #executed
        self._cppControler.startPhaseIncrementCalc(phaseIndex)
    
    """
      Whenever tls has a phase switch this method are called
    """
    def _onPhaseChange(self, step, phaseIndex) :
        print "phase changed {0} => {1} time({2})".format(self.currentPhase, phaseIndex, step - self.lastPhaseSwitchStep)
        
        self._onPhaseEnd(self.currentPhase, step)
        self._onPhaseStart(phaseIndex, step)
        
        self.lastPhaseSwitchStep = step
        self.currentPhase = phaseIndex
        
    def _collectData(self) :
        print "Collecting data."
        
        #Collect flow data
        #I need to get induction loop accumulated value, this value I need to
        #put on a street. To do this in an acceptable way I need the following
        #structure:
        #     { inductionLoopId : { street : <id>, value : <value> }
        #
        # Remembering that one or more induction loop contributes to value
        # for a unique accumulator. 
        #
        # Also, before store values for a street I need to reset previous values
        for streetName in self._streets :
            self._streets[streetName].resetFlow()
        
        for accumulator in self._flowAccumulators :
            accumulator['street'].addToFlow(accumulator['accumulatedValue'])
            accumulator['accumulatedValue'] = 0
        
        #Collect queue data
        for streetName in self._streets :
            
            queueSize = 0
            
            for lane in self._streets[streetName].getLanes() :
                queueSize += traci.lane.getLastStepVehicleNumber(lane)

            self._streets[streetName].setQueue(queueSize)
            self._cppControler.updateStreetSensors(streetName, self._streets[streetName].getFlow(), queueSize)
        
    
    def _accumulateFlowValue(self):
    
        for laneId in self._lane2FlowAccumulators :
            accumulator = self._lane2FlowAccumulators[laneId]['accumulator']
            inductionLoopId = self._lane2FlowAccumulators[laneId]['inductionLoopId']
            
            inductionValue = traci.inductionloop.getLastStepVehicleNumber(inductionLoopId)
            
            accumulator['accumulatedValue'] += inductionValue
            
    
    def _buildAccumulators(self): 
        
        print "Configurating induction loop monitors"
        
        inductionLoopList = traci.inductionloop.getIDList()
           
        for streetName in self._streets :
            street = self._streets[streetName]
            accumulator = { 'accumulatedValue' : 0, 'street' : street }
            
            for lane in street.getLanes() : 
                inductionLoopId = "e1det_" + lane 
                
                if not inductionLoopId in inductionLoopList:
                    print "WARNING No induction loop configured to lane {0} for tls {1}".format(lane, self.tlsId)
                    continue
                
                self._flowAccumulators.append(accumulator);
                self._lane2FlowAccumulators[lane] = { 'accumulator' : accumulator, 'inductionLoopId' : inductionLoopId }
    
    
    def _buildStreets(self, lanes):
        for lane in lanes :
            parsedLane = self._parseLaneName(lane)
            
            currentStreet = None
            
            # If street name was not found, we ignore this lane
            # TODO show some warning message
            if parsedLane["street"] == "" :
                continue 
            
            # Check if street was already included
            if parsedLane["street"] in self._streets :
                currentStreet = self._streets[parsedLane["street"]]
            
            # If we have a valide street name and is still not in our struture
            # we include it
            if currentStreet == None :
                currentStreet = Street(parsedLane["street"], Direction.N)
                self._streets[parsedLane["street"]] = currentStreet
                self._cppControler.addStreet(parsedLane["street"], Direction.N)
        
            if not currentStreet.containsLane(lane) :
                currentStreet.addLane(lane)
        
    def _parseLaneName(self, lane):
        #TODO use regex to find these things
        laneDef = {"street" : "" , "segmentNumber" : -1, "laneNumber" : -1}
        
        splitLaneNumber = lane.partition("_")
        
        laneDef["laneNumber"] = int(splitLaneNumber[2])
        
        splitSegNumber = splitLaneNumber[0].partition("Seg")
        
        laneDef["street"] = splitSegNumber[0]
        laneDef["segmentNumber"] = splitSegNumber[2]
        
        return laneDef
    
    def _dumpStreets(self) :
        strStreets = "{"
        for key in self._streets.keys() :
            strStreets += key + " : " + self._streets[key].__str__() + ","
            
        strStreets += "}"
        
        print strStreets
        

        